package com.xiaok.mobileredis.utils;

import org.checkerframework.checker.units.qual.A;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 二倍均值法随机生成红包金额
 */
public class RedPacketUtil {
    /**
     * 使用二倍均值法随机生成红包金额
     * 二倍均值法
     * 假设总金额是M元，N个人，每次抢的金额=(0, (M/N) *2)，比如，还是之前说的条件，金额100，人数10，
     *
     * 第一个人抢的金额是 (0,20)，抢到的数值，根据正态分布，应该是10左右，远低于10的概率很小，同样远大于10的概率和很小，这里假设第一个人抢到的数值是10；
     *
     * 第二个人抢的金额是(0,90/9 *2)=(0,20)，同第一个人，第二个人红包金额也应该是10附近；
     *
     * 剩下的人，以此类推。
     *
     * 查阅了“微信红包的架构设计”，里面就是使用的这个方法。但是，这个算法，也不是完美的，
     * 假如第一个人抢到15，第二个人的范围是(0,18.89)，假如第二个人又抢到很高，那对后面的人是不利的
     * @param totalAmount 红包总钱数
     * @param totalPeople 抢红包的总人数
     * @return
     */
    /**
     * 获取红包金额的方法
     *
     * @param count
     * @param money
     */
    public static List<Double> dividedRedPackage(double money, int count) {
        List<Double> list = new ArrayList<>();
        for (int i = 0; i <= count; i++) {
            //每个人分到的红包金额,除了最后一次
            double cash;
            //要先判断是不是最后一个人，如果是最后一个人则拿到剩下的所以红包
            if (i == count - 1) {
                list.add(money);
                break;
            }
            //利用二倍指数法获取每个人抽取红包金额的随机范围,剩余总金额在变，人数也在变
            double range = NumberUtils.format2Scale(money / (count - i)) * 2;
            cash = NumberUtils.format2Scale(Math.random() * range); //保留2位小数
            //如果这次抽到的小于0.01，则此次不算，跳过重新抽一次
            if (cash < 0.01) {
                i--;
                continue;
            }
            list.add(cash);
            System.out.println("第" + i + "个人抽到的红包：" + cash);
            //计算剩余总金额
            money = NumberUtils.sub(money, cash);
        }
        System.out.println("抽取红包的总金额：" + validPackage(list));
        return list;
    }

    /**
     * “线性切割法”的算法，这个算法的思想是：把总金额数值，想象成一条绳子，对绳子切割N-1刀(N就是人的数量)，
     * 每个人抢到的红包金额就是切割的绳子的占比，但是，这方法也不能避免“无限循环小数”问题；
     * 并且，对每个人抢到的金额尽量平均，该如何保证呢？需要考虑的点很多，时间复杂度、空间复杂度都比较高
     * 获取红包金额的方法
     * 使用线性分割的方法进行红包计算（在红包总金额的范围内生成红包切割点）
     *
     * @param money
     * @param count
     */
    public static List<Double> dividedRedPackage2(double money, int count) {
        ArrayList<Double> list = new ArrayList<>();
        //根据人数创建切割点 ,切换点的数量为 人数-1 .10个人只需9个切割点
        double[] array = new double[count - 1];
        for (int i = 0; i < array.length; i++) {
            //剩余总金额在变，所以切割点范围在变, random 0~1
            double point = NumberUtils.format2Scale(Math.random() * money);
            //判断数组中是否已经存在
            if (hasSameElement(array, point)) {
                i--;
                continue;
            }
            //判断生成的值是否小于0.01
            if (point < 0.01) {
                i--;
                continue;
            }
            //将符合条件的切割点添加至切割点数组
            array[i] = point;
        }
        //对红包切割点进行排序
        Arrays.sort(array);
        // 第一个红包
        for (int i = 0; i < count; i++) {
            double cash;
            if (i == 0) { //第一个红包
                cash = array[0];
            } else if (i == count - 1) { //最后一个红包
                cash = NumberUtils.sub(money, array[array.length - 1]);
            } else {  //当前切割点 - 前一个切割点 = 当前红包的值
                cash = NumberUtils.sub(array[i], array[i - 1]);
            }
            list.add(cash);
            System.out.println("第" + i + "个人分到的红包金额是" + cash);
        }
        System.out.println("总金额" + validPackage(list));
        return list;
    }

    /**
     * 判断数组中是否已经存在该分割点
     *
     * @param array
     * @param value
     * @return
     */
    public static boolean hasSameElement(double[] array, double value) {
        boolean flag = false;
        //遍历数组
        for (int i = 0; i < array.length; i++) {
            //判断数组中是否有重复
            if (array[i] == value) {
                //如果有则修改标识
                flag = true;
                break;
            }
        }
        return flag;
    }

    /**
     * 冒泡排序法，将红包截点进行排序
     *
     * @param array
     */
    public static void sort(double[] array) {
        for (int i = 1; i < array.length; i++) {
            for (int j = 0; j < array.length - i; j++) {
                if (array[j] > array[j + 1]) {
                    double temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }

    public static double validPackage(List<Double> list) {
        return list.stream().mapToDouble(Double::doubleValue).sum();
    }


    public static void main(String[] args) {
        //dividedRedPackage(100.44d,100);
        dividedRedPackage2(100.11, 100);
    }
}
